package domain.lexicalAnalyzerGenerator;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import domain.grammar.ExtendedGrammar;
import domain.grammar.ExtendedNonTerminalSymbol;
import domain.grammar.ExtendedTerminalSymbol;
import domain.regularExpression.NonUnaryRegularExpressionOperator;
import domain.regularExpression.RegularExpressionElement;
import domain.regularExpression.UnaryRegularExpressionOperator;

public abstract class Instruction {

	static ExtendedGrammar currentExtendedGrammar;

	private static String toString(Set<ExtendedTerminalSymbol> terminals){
		String res = "";

		Iterator<ExtendedTerminalSymbol> iterator = terminals.iterator();
		while(iterator.hasNext()){
			res += iterator.next().getName();
		}		
		return res;
	}

	static String getFirsts(RegularExpressionElement regularExpressionElement){
		return toString(currentExtendedGrammar.firsts(regularExpressionElement));
	}

	static List<String> getDirectiveSymbols(NonUnaryRegularExpressionOperator nonUnaryRegularExpressionOperator){
		List<String> directiveSymbols = new Vector<String>();

		List<RegularExpressionElement> subElements = nonUnaryRegularExpressionOperator.getSubElements();
		Iterator<RegularExpressionElement> itSubElem = subElements.iterator();
		while(itSubElem.hasNext()){
			RegularExpressionElement aSubElem = itSubElem.next();
			String aSubElemDS = Instruction.getFirsts(aSubElem);
			if(Instruction.isAnnulable(aSubElem)){
				aSubElemDS = aSubElemDS + Instruction.getNexts(aSubElem);
			}
			directiveSymbols.add(aSubElemDS);
		}
		return directiveSymbols;
	}
	
	private static String getNexts(RegularExpressionElement aRegExpElem){
		return toString((Set<ExtendedTerminalSymbol>)currentExtendedGrammar.nexts(aRegExpElem));
	}

	private static boolean isAnnulable(RegularExpressionElement subElement) {
		return currentExtendedGrammar.nullable(subElement);
	}

	static List<Instruction> getSubInstructions(RegularExpressionElement regularExpressionElement) 
	throws InstructionException {
		List<Instruction> subInstructions = new Vector<Instruction>();
		for (int i = 0; i < regularExpressionElement.getSubElements().size(); i++){
			subInstructions.add(Instruction.create(regularExpressionElement.getSubElements().get(i)));
		}
		return subInstructions;
	}


	public static Instruction create(RegularExpressionElement regularExpression) throws InstructionException
	{
		Instruction res;

		if(regularExpression instanceof ExtendedTerminalSymbol){
			res = new MatchInstruction((ExtendedTerminalSymbol) regularExpression);
		}else if(regularExpression instanceof ExtendedNonTerminalSymbol){
			res = new CallInstruction((ExtendedNonTerminalSymbol) regularExpression);
		}else if(regularExpression instanceof UnaryRegularExpressionOperator){
			UnaryRegularExpressionOperator unaryOp = (UnaryRegularExpressionOperator)regularExpression;
			if(unaryOp.isAsterisk()){
				res = new WhileInstruction(unaryOp);
			}else if(unaryOp.isInterrogation()){
				res = new StartsWithInstruction(unaryOp);
			}else if(unaryOp.isPlus()){
				res = new DoWhileInstruction(unaryOp);
			}else{
				throw new InstructionException();
			}
		}else if(regularExpression instanceof NonUnaryRegularExpressionOperator){
			NonUnaryRegularExpressionOperator nonUnaryOp = (NonUnaryRegularExpressionOperator)regularExpression;
			if(nonUnaryOp.isConcatenation()){
				res = new ChainCallInstruction(nonUnaryOp);
			}else if(nonUnaryOp.isPipe()){
				res = new SwitchInstruction(nonUnaryOp);
			}else{
				throw new InstructionException();
			}
		}else{
			throw new InstructionException();
		}
		return res;
	}

	public abstract String generateCode() throws CodeGenerationException;
}
